/**
 * @Title QRCodeUtil.java
 * @Package come
 * @Description TODO(用一句话描述该文件做什么)
 * @author LeiXiaoLong(1245920593 @ qq.com)
 * @date 2017年7月5日 下午5:56:17
 * @version V1.0
 */
package com.sunyard.utils;

import cn.hutool.extra.qrcode.BufferedImageLuminanceSource;
import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Base64;
import java.util.Hashtable;
import java.util.Random;

/**
 * @ClassName: QRCodeUtil
 * @Description TODO(这里用一句话描述这个类的作用)
 * @author LeiXiaoLong(1245920593 @ qq.com)
 * @date 2017年7月5日 下午5:56:17
 */
@Slf4j
public class QRCodeUtil {

    /**
     * Description
     */
    private static final String CHARSET = "utf-8";

    /**
     * Description
     */
    public static final String FORMAT_NAME = "JPG";

    //二维码尺寸
    private static final int QRCODE_SIZE = 479;

    //LOGO宽度
    private static final int WIDTH = 70;

    //LOGO高度
    private static final int HEIGHT = 70;

    //二维码距离是层的距离
    private static int bagHight = 347;

    //第一行文字距离上边的距离
    private static int contentHight = 865;

    //设置初始文字长度需要调整边距的值
    private static int reChangeLength = 50;

    /**
     * Description
     */
    private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
        Hashtable hints = new Hashtable();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix =
                new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        if (imgPath == null || "".equals(imgPath)) {
            return image;
        }
        //插入图片
        QRCodeUtil.insertImage(image, imgPath, needCompress);
        return image;
    }

    /**
     * Description
     */
    private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {
        File file = new File(imgPath);
        if (!file.exists()) {
            System.err.println("" + imgPath + "   该文件不存在！");
            return;
        }
        Image src = ImageIO.read(new File(imgPath));
        int width = src.getWidth(null);
        int height = src.getHeight(null);
        if (needCompress) { //压缩LOGO
            if (width > WIDTH) {
                width = WIDTH;
            }
            if (height > HEIGHT) {
                height = HEIGHT;
            }
            Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics g = tag.getGraphics();
            g.drawImage(image, 0, 0, null); //绘制缩小后的图
            g.dispose();
            src = image;
        }
        //插入LOGO
        Graphics2D graph = source.createGraphics();
        int x = (QRCODE_SIZE - width) / 2;
        int y = (QRCODE_SIZE - height) / 2;
        graph.drawImage(src, x, y, width, height, null);
        Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
        graph.setStroke(new BasicStroke(3f));
        graph.draw(shape);
        graph.dispose();
    }

    /**
     * Description
     */
    public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
        BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
        mkdirs(destPath);
        String file = new Random().nextInt(99999999) + ".jpg";
        ImageIO.write(image, FORMAT_NAME, new File(destPath + "/" + file));
    }

    /**
     * Description
     */
    public static void encode(String content, URL bac, String qrCode, InputStream in, String note, OutputStream output, boolean needCompress)
            throws Exception {
        BufferedImage image = QRCodeUtil.createImage(content, bac, qrCode, in, note, needCompress);
        ImageIO.write(image, FORMAT_NAME, output);
    }

    /**
     * 图片预览
     */
    public static String getBufferedImageToBase64(String content, URL bac, String qrCode, InputStream in, String note, OutputStream output, boolean needCompress, String imageFormatName) throws Exception {
        BufferedImage bufferedImage = QRCodeUtil.createImage(content, bac, qrCode, in, note, needCompress);
        if (StringUtils.isBlank(imageFormatName)) {
            imageFormatName = "png";
        }
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, imageFormatName, stream);
        String s = Base64.getEncoder().encodeToString(stream.toByteArray());
        return s;
    }

    /**
     * Description
     */
    private static BufferedImage createImage(String content, URL bac, String qrCode, InputStream in, String note, boolean needCompress) throws Exception {
        Hashtable hints = new Hashtable();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix =
                new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        //生成二维码信息
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //添加背景图
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        //插入logo
        QRCodeUtil.insertImage(image, qrCode, in, needCompress);
        //添加背景图
        image = insertBacgroundPic(image, bac, qrCode, note);
        return image;
    }

    /**
     *
     * @param image  二维码
     * @param qrCode 背景图片路径
     * @param note 描述文字
     * @return 加入背景图的二维码
     */
    public static BufferedImage insertBacgroundPic(BufferedImage image, URL bac, String qrCode, String note) throws Exception {
        if (null != qrCode) {
            log.info("预制码牌编号:{}", qrCode);
            return image;
        }
        try {
            //设置初始距离
            bagHight = 347;
            // contentHight=865;
            //当文字长度超过30个字时需要调整二维码的高度 取消文字展示
            if (bac != null && note.length() > QRCodeUtil.reChangeLength) {
                bagHight -= 34;
                contentHight -= 34;
            }
            //获取二维码（只适用于windos环境） logo流
//            Image src=Toolkit.getDefaultToolkit().getImage(bac);
//            BufferedImage backgroundImage=BufferedImageBuilder.toBufferedImage(src);
            ImageIcon src = new ImageIcon(bac);
            BufferedImage backgroundImage = new BufferedImage(src.getIconWidth(),
                    src.getIconHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics g = backgroundImage.createGraphics();
            g.setColor(Color.white);
            g.fillRect(0, 0, src.getIconWidth(), src.getIconHeight());
            g.drawImage(src.getImage(), 0, 0, null);
            g.dispose();
            log.info("去除遮盖");


            int bgWidth = backgroundImage.getWidth();
            log.info("bgWidth:{}", bgWidth);
            int qrWidth = image.getWidth();
            //距离背景图片x边的距离，居中显示
            int disx = (bgWidth - qrWidth) / 2;
            //距离y边距离 * * * *
            int disy = bagHight;
            Graphics2D rng = backgroundImage.createGraphics();
            rng.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f));
            rng.drawImage(image, disx, disy, image.getWidth(), image.getHeight(), null);
            //设置描述文字
            //设置字体类型和大小(BOLD加粗/ PLAIN平常) 取消文字描述
            rng.setFont(new Font("宋体", Font.PLAIN, 30));
            //设置字体颜色
            rng.setColor(Color.white);
            //开始设置文字显示位置
            //计算循环次数
            int count = 0;
            if (note.length() % 25 != 0) {
                count = note.length() / 25 + 1;
            } else {
                count = note.length() / 25;
            }
            for (int i = 0; i < count; i++) {
                if (i == count - 1) {
                    int strWidth = rng.getFontMetrics().stringWidth(note.substring(i * 25));
                    int disx1 = (bgWidth - strWidth) / 2;
                    rng.drawString(note.substring(i * 25), disx1, contentHight + i * 34);
                } else {
                    int strWidth = rng.getFontMetrics().stringWidth(note.substring(0, 25));
                    int disx1 = (bgWidth - strWidth) / 2;
                    rng.drawString(note.substring(i * 25, (i + 1) * 25), disx1, contentHight + i * 34);
                }
            }
            rng.dispose();
            image = backgroundImage;
            image.flush();
        } catch (Exception e) {
            log.info("插入背景图出错，原因是：{}", e.getMessage());
        }
        return image;
    }

    /**
     * Description
     */
    private static void insertImage(BufferedImage source, String qrCode, InputStream in, boolean needCompress) throws Exception {
        if (null == in) {
            System.err.println("二维码logo文件不存在！");
            return;
        }
        Image src = ImageIO.read(in);
        int width = src.getWidth(null);
        int height = src.getHeight(null);
        if (needCompress) { //压缩LOGO
            if (width > WIDTH) {
                width = WIDTH;
            }
            if (height > HEIGHT) {
                height = HEIGHT;
            }
            Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics g = tag.getGraphics();
            g.drawImage(image, 0, 0, null); //绘制缩小后的图
            g.dispose();
            src = image;
        }
        //插入LOGO
        Graphics2D graph = source.createGraphics();
        int x = (QRCODE_SIZE - width) / 2;
        int y = (QRCODE_SIZE - height) / 2;
        graph.drawImage(src, x, y, width, height, null);
        Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
        graph.setStroke(new BasicStroke(3f));
        graph.draw(shape);
        //定义二维码字体样式
        /** 设置生成图片的文字样式 * */
        if (null != qrCode) {
            Font font = new Font("黑体", 5, 32);
            graph.setFont(font);
            graph.setPaint(Color.BLACK);
            FontRenderContext context = graph.getFontRenderContext();
            Rectangle2D bounds = font.getStringBounds(qrCode, context);
            double m = (QRCODE_SIZE - bounds.getWidth()) / 2;
            double n = (QRCODE_SIZE + 2);
            graph.drawString(qrCode, (int) m, (int) n);
        }
        graph.dispose();
    }

    /**
     * Description
     */
    public static void mkdirs(String destPath) {
        File file = new File(destPath);
        //当文件夹不存在时，mkdirs会自动创建多层目录，区别于mkdir．(mkdir如果父目录不存在则会抛出异常)
        if (!file.exists() && !file.isDirectory()) {
            file.mkdirs();
        }
    }

    /**
     * Description
     */
    public static void encode(String content, String imgPath, String destPath) throws Exception {
        QRCodeUtil.encode(content, imgPath, destPath, false);
    }

    /**
     * Description
     */
    public static void encode(String content, String destPath, boolean needCompress) throws Exception {
        QRCodeUtil.encode(content, null, destPath, needCompress);
    }

    /**
     * Description
     */
    public static void encode(String content, String destPath) throws Exception {
        QRCodeUtil.encode(content, null, destPath, false);
    }

    /**
     * Description
     */
    public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)
            throws Exception {
        BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
        ImageIO.write(image, FORMAT_NAME, output);
    }

    /**
     * Description
     */
    public static void encode(String content, OutputStream output) throws Exception {
        QRCodeUtil.encode(content, null, output, false);
    }

    /**
     * Description
     */
    public static String decode(File file) throws Exception {
        BufferedImage image;
        image = ImageIO.read(file);
        if (image == null) {
            return null;
        }
        BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Result result;
        Hashtable hints = new Hashtable();
        hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
        result = new MultiFormatReader().decode(bitmap, hints);
        String resultStr = result.getText();
        return resultStr;
    }

    /**
     * Description
     */
    public static String decode(String path) throws Exception {
        return QRCodeUtil.decode(new File(path));
    }

    /**
     * Description
     */
    public static void main(String[] args) throws Exception {
        String text = "123";
        QRCodeUtil.encode(text, "C:/Users/LXL/Desktop/IMG_1214.jpg", "d:/MyWorkDoc", true);
    }

}